package cn.com.twoke.game.client.games.gomoku;

import cn.com.twoke.game.client.games.framework.core.AcceptChatMessageAble;
import cn.com.twoke.game.client.games.framework.core.Game;
import cn.com.twoke.game.client.games.framework.core.GamePanel;
import cn.com.twoke.game.client.games.framework.ui.ChatMessage;
import cn.com.twoke.game.client.games.framework.ui.MessageListJPanel;
import cn.com.twoke.game.client.games.gomoku.constants.GomokuGameConstant;
import cn.com.twoke.game.client.games.gomoku.controller.GomokuController;
import cn.com.twoke.game.client.games.gomoku.controller.RobotController;
import cn.com.twoke.game.client.games.gomoku.entity.Chess;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Arc2D;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;

public class GomokuGame extends Game implements GomokuGameConstant
        , MouseListener, MouseMotionListener, AcceptChatMessageAble {

    public static final String ID = "gomoku";
    public static final String TITLE = "五子棋";

    private Chess[][] grid;
    private Stack<Chess> chessNotes;
    private GomokuController controller;
    private boolean canDropChess = true;



    public GomokuGame(GomokuController controller) {
        super(GomokuGame::buildGamePanel);
        panel.addMouseMotionListener(this);
        panel.addMouseListener(this);
        this.controller = controller;
        this.controller.setGame(this);
    }


    private MessageListJPanel messageListJPanel;
    private JScrollPane scrollPane;

    private List<ChatMessage> messages;

    private JLabel chessTypeLabel;

    @Override
    protected void withComponent(JFrame frame) {
        this.canDropChess = true;
        this.dropingChess = new Chess(-1, -1);
        this.dropingChess.setType(Chess.Type.BLACK);

        frame.add(panel, BorderLayout.CENTER);
        JPanel optionPanel = new JPanel();
        optionPanel.setPreferredSize(new Dimension(280,  WINDOW_HEIGHT));
        this.messages = new ArrayList<>();
        messageListJPanel = new MessageListJPanel(this, this.messages);
        scrollPane = new JScrollPane(messageListJPanel);
        final JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
        verticalScrollBar.setValue(verticalScrollBar.getMaximum());
        scrollPane.setPreferredSize(new Dimension(260,  500));
        // 设置滚动条的显示策略，确保在需要时显示滚动条
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        optionPanel.add(scrollPane, BorderLayout.NORTH);

        JPanel inputPanel = new JPanel();
        inputPanel.setLayout(new GridLayout(3, 1,0,10));
        /**
         * 发送消息
         */
        JButton sendMessageBtn = new JButton("发消息");
        sendMessageBtn.setPreferredSize(new Dimension(260, 38));
        sendMessageBtn.addActionListener(e -> {
            String content = JOptionPane.showInputDialog(this.getWindow(),"请输入发送的消息");
            if (null == content || "".equals(content)) {
                return;
            }
            controller.sendMessage(content);
        });
        inputPanel.add(sendMessageBtn);


        /**
         * 悔棋
         */
        JButton undoBtn = new JButton("悔棋");
        undoBtn.setPreferredSize(new Dimension(260, 38));
        inputPanel.add(undoBtn);
        undoBtn.addActionListener(e -> {
            if (this.chessNotes.isEmpty()) {
                JOptionPane.showMessageDialog(this.getWindow(), "没有可悔棋了");
                return;
            }
            controller.undo();
        });

        /**
         * 结束游戏
         */
        JButton gameOverBtn = new JButton("结束游戏");
        gameOverBtn.setPreferredSize(new Dimension(260, 38));
        gameOverBtn.addActionListener(e -> {
            controller.gameOver();
        });
        inputPanel.add(gameOverBtn);
        optionPanel.add(inputPanel, BorderLayout.CENTER);
        chessTypeLabel = new JLabel("");
        updateChessLabel();
        optionPanel.add(chessTypeLabel, BorderLayout.SOUTH);
        frame.add(optionPanel, BorderLayout.EAST);
        frame.pack();
    }

    public void updateChessLabel() {
        chessTypeLabel.setText((dropingChess.getType() == Chess.Type.BLACK ?  "执黑棋" : "执白棋" )+ "  " + (canDropChess ? "请选择落子" : "请等待对方落子"));
    }


    @Override
    public void acceptMessage(ChatMessage chatMessage) {
        messageListJPanel.addMessage(chatMessage);
        SwingUtilities.invokeLater(() -> {
            scrollPane.getViewport().setViewPosition(new Point(0,
                    messageListJPanel.getHeight()));
        });
    }

    @Override
    public String getId() {
        return ID;
    }

    @Override
    protected void initialzer() {
        // 初始化棋盘
        grid = new Chess[ROW][COL];
        for (int row = 0; row < ROW; row++) {
            for (int col = 0; col < COL; col++) {
                grid[row][col] = new Chess(col, row);
                this.add(grid[row][col]);
            }
        }
        // 操作记录
        chessNotes = new Stack<>();
    }

    private static GamePanel buildGamePanel(Game game) {
        return new GamePanel(game, WINDOW_WIDTH, WINDOW_HEIGHT);
    }

    @Override
    protected String getTitle() {
        return withUserName(TITLE);
    }

    @Override
    public void update(double deltaU) {
    }




    @Override
    public void render(Graphics2D g2d) {
        renderGrid(g2d);
        renderDroppedChess(g2d);
        renderDroppingChess(g2d);
    }


    @Override
    public void initGameState() {
        if (this.isRoomMaster) {
            dropingChess.setType(Chess.Type.BLACK);
        } else {
            dropingChess.setType(Chess.Type.WHITE);
            this.canDropChess = false;
            this.updateChessLabel();
        }
    }

    /**
     * 绘制正在下棋子
     * @param g2d
     */
    private void renderDroppingChess(Graphics2D g2d) {
        if (dropingChess.isDisplay()) {
            g2d.setColor(new Color(255, 0, 0, 0x88));
            g2d.fill(dropingChess.getHitbox());
        }
    }

    /**
     * 绘制已下棋子
     * @param g2d
     */
    private void renderDroppedChess(Graphics2D g2d) {
        super.render(g2d);
    }

    public void setDroppingChessType(Chess.Type type) {
        this.dropingChess.setType(type);
    }


    /**
     * 绘制棋盘
     * @param g2d
     */
    public void renderGrid(Graphics2D g2d) {
        g2d.setColor(new Color(0xDEB887));
        g2d.fill(new Rectangle(0, 0,
                WINDOW_WIDTH - PANEL_PADDING_RIGHT + PANEL_PADDING_LEFT,
                WINDOW_HEIGHT));

        g2d.setColor(Color.BLACK);
        for (int x = 0; x < COL; x++) {
            g2d.drawLine(PANEL_PADDING_LEFT + CELL_WIDTH * x, PANEL_PADDING_TOP,
                    PANEL_PADDING_LEFT + CELL_WIDTH * x,
                    WINDOW_HEIGHT - PANEL_PADDING_BOTTOM);
        }
        for (int y = 0; y < ROW; y++) {
            g2d.drawLine(PANEL_PADDING_LEFT, PANEL_PADDING_TOP + CELL_HEIGHT * y,
                    WINDOW_WIDTH - PANEL_PADDING_RIGHT,
                    PANEL_PADDING_TOP + CELL_HEIGHT * y);
        }


        g2d.setColor(Color.RED);
        Arc2D arc = new Arc2D.Double(PANEL_PADDING_LEFT + CELL_WIDTH * 3 - 5,
                PANEL_PADDING_TOP + CELL_HEIGHT * 3 - 5, 10, 10, 0, 360,  Arc2D.PIE);
        g2d.fill(arc);

        arc = new Arc2D.Double(PANEL_PADDING_LEFT + CELL_WIDTH * 3 - 5,
                PANEL_PADDING_TOP + CELL_HEIGHT * 11 - 5, 10, 10, 0, 360,  Arc2D.PIE);
        g2d.fill(arc);

        arc = new Arc2D.Double(PANEL_PADDING_LEFT + CELL_WIDTH * 11 - 5,
                PANEL_PADDING_TOP + CELL_HEIGHT * 3 - 5, 10, 10, 0, 360,  Arc2D.PIE);
        g2d.fill(arc);

        arc = new Arc2D.Double(PANEL_PADDING_LEFT + CELL_WIDTH * 11 - 5,
                PANEL_PADDING_TOP + CELL_HEIGHT * 11 - 5, 10, 10, 0, 360,  Arc2D.PIE);
        g2d.fill(arc);


        arc = new Arc2D.Double(PANEL_PADDING_LEFT + CELL_WIDTH * 7 - 5,
                PANEL_PADDING_TOP + CELL_HEIGHT * 7 - 5, 10, 10, 0, 360,  Arc2D.PIE);
        g2d.fill(arc);
    }

    public static void main(String[] args) {
        new GomokuGame(new RobotController()).show();
    }





    @Override
    public void mouseDragged(MouseEvent e) {}



    @Override
    public void mouseMoved(MouseEvent e) {
        dealDroppingChess(e);
    }


    Chess dropingChess;
    /**
     * 处理落棋
     * @param e
     */
    private void dealDroppingChess(MouseEvent e) {
        if (!canDropChess) return;
        boolean showDroppingChess = false;
        if (0 < e.getY() && e.getY() <= WINDOW_HEIGHT - PANEL_PADDING_BOTTOM  + 30&&
                0 < e.getX() && e.getX() <= WINDOW_WIDTH - PANEL_PADDING_RIGHT + 30
        ) {
            for (int row = 0; row < ROW; row++) {
                for (int col = 0; col < COL; col++) {
                    if (!grid[row][col].isDrop() && grid[row][col].getHitbox().getBounds2D().contains(e.getX(), e.getY())) {
                        dropingChess.setPosition(col, row);
                        dropingChess.drop(dropingChess.getType());
                        showDroppingChess = true;
                        break;
                    }
                }
            }
        }
        if (!showDroppingChess) {
            dropingChess.hide();
        }
    }

    public Stack<Chess> getChessNotes() {
        return chessNotes;
    }

    public void setChessNotes(Stack<Chess> chessNotes) {
        this.chessNotes = chessNotes;
    }

    public Chess[][] getGrid() {
        return grid;
    }

    public void setCanDropChess(boolean canDropChess) {
        this.canDropChess = canDropChess;
    }

    public void setGrid(Chess[][] grid) {
        this.grid = grid;
    }

    public boolean isCanDropChess() {
        return canDropChess;
    }

    public Chess.Type getUserDropingChessType() {
        return dropingChess.getType();
    }

    public Chess.Type getNotUserDropingChessType() {
        return Chess.Type.BLACK == dropingChess.getType() ? Chess.Type.WHITE : Chess.Type.BLACK;
    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (canDropChess && dropingChess.getHitbox().getBounds2D().contains(e.getX(), e.getY())) {
            dropingChess.hide();
            controller.drop(dropingChess.getPosition().x,
                    dropingChess.getPosition().y);
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

    public void drop(int x, int y, Chess.Type type) {
        this.grid[y][x].drop(type);
        this.chessNotes.push(this.grid[y][x]);
    }

    public boolean checkWin(int x, int y, Chess.Type type) {
        // 检查横向
        int count = 1;
        for (int i = x - 1; i >= 0 && grid[y][i].getType() == type; i--) count++;
        for (int i = x + 1; i < 15 && grid[y][i].getType() == type; i++) count++;
        if (count >= 5) return true;
        // 检查纵向
        count = 1;
        for (int i = y - 1; i >= 0 && grid[i][x].getType() == type; i--) count++;
        for (int i = y + 1; i < 15 && grid[i][x].getType() == type; i++) count++;
        if (count >= 5) return true;
        // 检查正斜向
        count = 1;
        for (int i = y - 1, j = x - 1; i >= 0 && j >= 0 && grid[i][j].getType() == type; i--, j--) count++;
        for (int i = y + 1, j = x + 1; i < 15 && j < 15 && grid[i][j].getType() == type; i++, j++) count++;
        if (count >= 5) return true;
        // 检查反斜向
        count = 1;
        for (int i = y - 1, j = x + 1; i >= 0 && j < 15 && grid[i][j].getType() == type; i--, j++) count++;
        for (int i = y + 1, j = x - 1; i < 15 && j >= 0 && grid[i][j].getType() == type; i++, j--) count++;
        return count >= 5;
    }


    public void resetGame() {
        for (int i = 0; i < 15; i++) {
            for (int j = 0; j < 15; j++) {
                grid[i][j].setType(Chess.Type.NONE);
            }
        }
        canDropChess = true;
        this.chessNotes.clear();
    }
}
